﻿namespace CIK.Framework.Diagnostics
{
    using System;
    using System.Configuration;
    using System.Diagnostics;

    /// <summary>
    /// Utility class for throwing exceptions.
    /// </summary>
    public static partial class Throw
    {
        #region Public Methods and Operators

        /// <summary>
        /// Will throw exception of type <typeparamref name="TException"/>
        /// with the specified message if the assertion is true.
        /// </summary>
        /// <typeparam name="TException">
        /// The type of specific Exception.
        /// </typeparam>
        /// <param name="assertion">
        /// If set to <c>true</c> [assertion].
        /// </param>
        /// <param name="message">
        /// The message.
        /// </param>
        /// <example>
        /// Sample usage:
        /// <code>
        /// <![CDATA[
        /// NskGuard.Against<ArgumentException>(string.IsNullOrEmpty(name), "Name must have a value");
        /// ]]>
        /// </code>
        /// </example>
        public static void Against<TException>(bool assertion, string message) where TException : Exception
        {
            if (assertion == false)
            {
                return;
            }

            throw (TException)Activator.CreateInstance(typeof(TException), message);
        }

        /// <summary>
        /// Throws a <see cref="ConfigurationErrorsException"/> with the specified message.
        /// </summary>
        /// <param name="message">
        /// The message to throw.
        /// </param>
        /// <param name="modifier">
        /// A modifier delegate used to modify the exception before being thrown.
        /// </param>
        /// <exception cref="ConfigurationErrorsException">
        /// The exception to throw.
        /// </exception>
        public static void ConfigurationErrors(string message, Func<Exception, Exception> modifier = null)
        {
            ThrowInternal(new ConfigurationErrorsException(message), modifier);
        }

        /// <summary>
        /// Throws a <see cref="InvalidOperationException"/> with the specified message.
        /// </summary>
        /// <param name="message">
        /// The message to throw.
        /// </param>
        /// <param name="modifier">
        /// A modifier delegate used to modify the exception before being thrown.
        /// </param>
        /// <exception cref="InvalidOperationException">
        /// The exception to throw.
        /// </exception>
        public static void InvalidOperation(string message, Func<Exception, Exception> modifier = null)
        {
            ThrowInternal(new InvalidOperationException(message), modifier);
        }

        /// <summary>
        /// Throws a <see cref="InvalidOperationException"/> with the specified message if the value is null or whitespace.
        /// </summary>
        /// <param name="value">
        /// The value to check.
        /// </param>
        /// <param name="message">
        /// The message to throw.
        /// </param>
        /// <param name="modifier">
        /// A modifier delegate used to modify the exception before being thrown.
        /// </param>
        /// <exception cref="InvalidOperationException">
        /// The exception to throw.
        /// </exception>
        public static void InvalidOperationIfValueIsNullOrWhiteSpace(
            string value, string message, Func<Exception, Exception> modifier = null)
        {
            if (string.IsNullOrWhiteSpace(value))
            {
                ThrowInternal(new InvalidOperationException(message), modifier);
            }
        }

        /// <summary>
        /// Throws a <see cref="NotSupportedException"/> with the specified message.
        /// </summary>
        /// <param name="message">
        /// The message to throw.
        /// </param>
        /// <param name="modifier">
        /// A modifier delegate used to modify the exception before being thrown.
        /// </param>
        /// <exception cref="NotSupportedException">
        /// The exception to throw.
        /// </exception>
        public static void NotSupported(string message, Func<Exception, Exception> modifier = null)
        {
            ThrowInternal(new NotSupportedException(message), modifier);
        }

        #endregion

        #region Methods

        /// <summary>
        /// Throws the specified exception.
        /// </summary>
        /// <param name="exception">
        /// The exception to be thrown.
        /// </param>
        /// <param name="modifier">
        /// A modifier delegate used to modify the exception before being thrown.
        /// </param>
        [DebuggerStepThrough]
        private static void ThrowInternal(Exception exception, Func<Exception, Exception> modifier = null)
        {
            if (exception == null)
            {
                return;
            }

            Exception ex = null;
            if (modifier != null)
            {
                ex = modifier(exception);
            }

            /* We should never try and suppress an exception at this point, so make sure the original
             * exception is thrown if the modifier function returns null. */
            if (ex == null)
            {
                ex = exception;
            }

            throw ex;
        }

        #endregion
    }
}